package com.googlecode.torrent4j.bencoding;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class BEEncoder
{
	ByteArrayOutputStream stream = new ByteArrayOutputStream();

	static final String UTF8 = "utf-8";

	public byte[] encode(BEValue value)
	{
		try
		{
			innerEncode(value);
			return stream.toByteArray();
		}
		catch (IOException e)
		{
			throw new InvalidBEEncodingException("Error encoding value", e);
		}
	}

	public void innerEncode(BEValue value) throws IOException
	{
		Object innerValue = value.getValue();

		if (innerValue instanceof byte[])
		{
			encodeByteArray((byte[]) innerValue);
		}
		else if (innerValue instanceof Number)
		{
			encodeNumber((Number) innerValue);
		}
		else if (innerValue instanceof List)
		{
			encodeList((List<BEValue>) innerValue);
		}
		else if (innerValue instanceof Map)
		{
			encodeMap((Map<String, BEValue>) innerValue);
		}
		else
		{
			throw new InvalidBEEncodingException("Unnable to encode, wrong type: " + innerValue.getClass().toString());
		}

	}

	private void encodeByteArray(byte[] innerValue) throws IOException
	{
		stream.write((Integer.toString(innerValue.length) + ":").getBytes(UTF8));
		stream.write(innerValue);
	}

	private void encodeNumber(Number innerValue) throws IOException
	{
		stream.write(("i" + innerValue.toString() + "e").getBytes(UTF8));
	}

	private void encodeList(List<BEValue> innerValue) throws IOException
	{
		stream.write("l".getBytes(UTF8));
		for (BEValue value : innerValue)
		{
			innerEncode(value);
		}
		stream.write("e".getBytes(UTF8));
	}

	private void encodeMap(Map<String, BEValue> innerValue) throws IOException
	{
		stream.write("d".getBytes(UTF8));
		for (Entry<String, BEValue> entry : innerValue.entrySet())
		{
			innerEncode(new BEValue(entry.getKey()));
			innerEncode(entry.getValue());
		}
		stream.write("e".getBytes(UTF8));
	}

}
